package com.jier.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.StringUtil;
import com.jier.common.Page;
import com.jier.common.Result;
import com.jier.dao.BaseDao;
import com.jier.dto.BaseParamDto;
import com.jier.entities.BaseEntity;
import com.jier.service.IBaseService;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.entity.Example;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static java.math.BigDecimal.ROUND_UP;

/**
 * 基础service
 *
 * @author jie_r
 * @Created by lj on 2016/9/13.
 */
public class BaseServiceImpl<M extends BaseDao<T>, T extends BaseEntity> implements IBaseService<T> {
    @Autowired
    protected M mapper;

    @Autowired
    protected SqlSessionFactory sessionFactory;

    /**
     * 接受泛型类型
     */
    private Class clazz;

    /**
     * id属性
     */
    private static final String ID = "id";

    /**
     * 获取子类传递的具体泛型类型
     */
    public BaseServiceImpl() {
        // 1、获取子类的class(在创建子类对象的时候,会返回父类的构造方法)
        Class<? extends BaseServiceImpl> clazz = this.getClass();
        // 2、获取当前类的带有泛型的父类类型
        ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
        // 3、返回实际参数类型(泛型可以写多个)
        Type[] types = type.getActualTypeArguments();
        // 4、获取第二个参数(泛型的具体类)
        this.clazz = (Class) types[1];
    }

    /**
     * 获取sqlSession
     *
     * @return
     * @throws Exception
     */
    public SqlSessionFactory getSqlSession() {
        return this.sessionFactory;
    }

    /**
     * 获取当前 Example 实例化
     *
     * @return
     */
    public Example getExample() {
        return new Example(this.clazz);
    }

    /**
     * 获取pageHelper分页对象
     *
     * @param page
     * @param <E>
     * @return
     */
    public <E> com.github.pagehelper.Page<E> getPageHelper(Page page) {
        return PageHelper.startPage(page.getCurPage(), page.getRowPerPage(), true);
    }

    /**
     * 根据ID获取数据
     *
     * @param id
     * @return
     * @throws Exception
     */
    @Override
    public T getById(Object id) throws Exception {
        return mapper.selectByPrimaryKey(id);
    }

    /**
     * 获取全部数据
     *
     * @return
     * @throws Exception
     */
    @Override
    public List<T> getAll() throws Exception {
        return mapper.selectAll();
    }

    /**
     * 根据条件获取数据集合
     *
     * @param example
     * @return
     * @throws Exception
     */
    @Override
    public List<T> getByExample(Example example) throws Exception {
        return mapper.selectByExample(example);
    }

    /**
     * 根据条件获取唯一数据
     *
     * @param example
     * @return
     * @throws Exception
     */
    @Override
    public T getUniqueByExample(Example example) throws Exception {
        List<T> users = getByExample(example);
        if (users == null || users.size() <= 0) {
            return null;
        }
        return users.get(0);
    }

    /**
     * 根据ID删除数据
     *
     * @param id
     * @throws Exception
     */
    @Override
    public int deleteById(Object id) throws Exception {
        return mapper.deleteByPrimaryKey(id);
    }

    /**
     * 根据ID集删除数据
     *
     * @param ids
     * @throws Exception
     */
    @Override
    public int delByIds(String ids) throws Exception {
        if (StringUtil.isEmpty(ids)) {
            return 0;
        }
        // 判断泛型实体中id字段类型是否为String，如果是，需要转换ids为（'','',''）格式
        if (this.clazz.getSuperclass().getDeclaredField(ID).getType() == String.class) {
            ids = "'".concat(ids.replaceAll(",", "','")).concat("'");
        }
        return mapper.deleteByIds(ids);
    }

    /**
     * 根据条件删除数据
     *
     * @param example
     * @throws Exception
     */
    @Override
    public int deleteByExample(Example example) throws Exception {
        return mapper.deleteByExample(example);
    }

    /**
     * 根据实体更新数据
     *
     * @param obj
     * @throws Exception
     */
    @Override
    public int updateById(T obj) throws Exception {
        obj.setUpdateDate(new Date());
        return mapper.updateByPrimaryKey(obj);
    }

    /**
     * 保存实体数据
     *
     * @param obj
     * @throws Exception
     */
    @Override
    public int save(T obj) throws Exception {
        return mapper.insert(obj);
    }

    /**
     * 保存实体集合，默认为空，需要时重写
     *
     * @param list
     * @throws Exception
     */
    @Override
    public int savAll(List<T> list) throws Exception {
        return mapper.insertList(list);
    }

    /**
     * 分页查询
     *
     * @param page
     * @param example
     * @return
     * @throws Exception
     */
    public Result<T> getRecordPage(Page page, Example example) throws Exception {
        // 分页条件
        RowBounds rowBounds = new RowBounds(page.getCurPage(), page.getRowPerPage());
        Integer count = mapper.selectCountByExample(example);
        if (count.equals(BigDecimal.ZERO)) {
            page.setTotalPages(0);
            page.setTotalRows(0);
            return new Result<T>(page, new ArrayList<T>());
        }
        List<T> list = mapper.selectByExampleAndRowBounds(example, rowBounds);
        if (com.jier.common.util.StringUtil.isEmptyList(list)) {
            page.setTotalPages(0);
            page.setTotalRows(0);
            return new Result<T>(page, new ArrayList<T>());
        }
        Integer pageCount = new BigDecimal(count).divide(new BigDecimal(page.getRowPerPage()), ROUND_UP).intValue();
        page.setTotalPages(pageCount);
        page.setTotalRows(count);
        return new Result<T>(page, list);
    }

    /**
     * 分页查询，插件使用PageHelper，见https://github.com/pagehelper/Mybatis-PageHelper
     *
     * @param sqlId
     * @param page
     * @param params
     * @return Result
     * @throws Exception
     */
    public Result getRecordPage(String sqlId, Page page, BaseParamDto params) throws Exception {
        Method method;
        try {
            // 根据sqlId反射获取对应方法
            method = mapper.getClass().getMethod(sqlId, BaseParamDto.class);
        } catch (Exception e) {
            throw new NoSuchMethodException(sqlId + "目标方法未定义");
        }
        try {
            com.github.pagehelper.Page<?> listPage = PageHelper.startPage(page.getCurPage(), page.getRowPerPage(), true);
            // 执行方法，查询数据
            method.invoke(mapper, params);
            return getRecordPage(listPage, page);
        } catch (Exception e) {
            throw new RuntimeException("系统内部错误:" + e.getMessage());
        }
    }

    /**
     * 返回分页数据
     *
     * @param listPage
     * @param page
     * @return
     * @throws Exception
     */
    public Result getRecordPage(com.github.pagehelper.Page<?> listPage, Page page) {
        page.setTotalPages(listPage.getPages());
        page.setTotalRows(new Long(listPage.getTotal()).intValue());
        return new Result<>(page, listPage.getResult());
    }
}

